Что такое providers в NestJS?

JuniorNestJS · Backend·Обновлено 12 сентября 2026
Коротко
Providers — это классы (обычно сервисы, репозитории, фабрики), помеченные декоратором @Injectable(), которые NestJS создаёт и внедряет через встроенный механизм Dependency Injection. Они регистрируются в массиве providers модуля и инкапсулируют бизнес-логику, отделяя её от контроллеров.

Providers в NestJS

Provider — это фундаментальное понятие NestJS, лежащее в основе механизма Dependency Injection (DI). Провайдером может быть сервис, репозиторий, фабрика, хелпер — по сути любой класс или значение, которое нужно внедрить (inject) в другой класс: контроллер, другой provider или модуль.

Главная идея в том, что NestJS сам создаёт экземпляры провайдеров и управляет их жизненным циклом, а разработчику не нужно писать new Service() вручную. Это снижает связанность кода и упрощает тестирование, так как зависимости легко подменить моками.

Как объявить provider

Обычный класс становится провайдером после декоратора @Injectable():

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  findAll() {
    return ['Alice', 'Bob'];
  }
}

Затем его нужно зарегистрировать в модуле в массиве providers:

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

После регистрации NestJS может внедрить UsersService через конструктор в любой класс того же модуля (или другого, если сервис экспортирован через exports).

Виды провайдеров

Помимо обычных классов, Nest поддерживает кастомные провайдеры с явным указанием токена:

  • useClass — указать, какой класс использовать для токена (полезно для подмены реализации по условию, например в тестах).
  • useValue — предоставить готовое значение (константу, мок, конфиг-объект).
  • useFactory — создать значение через фабричную функцию, которая может принимать другие зависимости.
  • useExisting — создать алиас на уже существующий provider.
@Module({
  providers: [
    {
      provide: 'CONFIG',
      useFactory: () => ({ apiUrl: process.env.API_URL }),
    },
  ],
})
export class AppModule {}

Область видимости (scope)

По умолчанию providers — синглтоны: один экземпляр на всё приложение, переиспользуемый во всех запросах. Это можно изменить через scope:

  • Scope.DEFAULT — singleton (по умолчанию);
  • Scope.REQUEST — новый экземпляр на каждый входящий запрос;
  • Scope.TRANSIENT — новый экземпляр при каждом внедрении.

Экспорт провайдеров

Чтобы provider был доступен в другом модуле, его нужно добавить в exports того модуля, где он объявлен, и импортировать сам модуль (imports) в модуль-потребитель.

Зачем это нужно

DI через providers позволяет держать бизнес-логику отдельно от HTTP-слоя (контроллеров), переиспользовать сервисы между модулями и легко подменять зависимости в unit-тестах через Test.createTestingModule и overrideProvider.

Что хочет услышать интервьюер

Понимание, что providers — это механизм Dependency Injection в NestJS

Знание декоратора @Injectable() и обязательной регистрации в массиве providers модуля

Понимание, что по умолчанию provider — singleton в рамках приложения

Знание, что для использования в другом модуле provider нужно экспортировать через exports

Представление о кастомных провайдерах: useClass, useValue, useFactory, useExisting

Пример: Базовый provider и его внедрение в контроллер

// users.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  private readonly users = ['Alice', 'Bob'];

  findAll(): string[] {
    return this.users;
  }
}

// users.controller.ts
import { Controller, Get } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  // Nest сам создаст и подставит экземпляр UsersService
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}

// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService], // регистрация провайдера
  exports: [UsersService], // делает провайдер доступным для других модулей
})
export class UsersModule {}

Пример: Кастомный provider через useFactory

@Module({
  providers: [
    {
      provide: 'APP_CONFIG',
      useFactory: () => ({
        apiUrl: process.env.API_URL ?? 'http://localhost:3000',
      }),
    },
  ],
  exports: ['APP_CONFIG'],
})
export class ConfigModule {}

// Внедрение по строковому токену через @Inject
import { Inject, Injectable } from '@nestjs/common';

@Injectable()
export class ApiService {
  constructor(@Inject('APP_CONFIG') private readonly config: { apiUrl: string }) {}
}

Типичные ошибки

Создают экземпляр сервиса вручную через new вместо использования DI

Забывают добавить сервис в массив providers модуля, из-за чего Nest не может его резолвить

Путают providers и exports — не понимают, что для доступа из другого модуля нужен именно exports

Считают, что каждый inject создаёт новый экземпляр сервиса, не зная про singleton scope по умолчанию

Не знают о существовании кастомных провайдеров (useValue/useFactory) и решают всё через обычные классы

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее